package org.nicocube.airain.ds;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.nicocube.airain.utils.config.Config;
import org.nicocube.airain.utils.config.ConfigProvider;
import org.nicocube.airain.utils.server.Server;
import org.nicocube.airain.utils.telnet.Command;
import org.nicocube.airain.utils.telnet.CommandDescription;
import org.nicocube.airain.utils.telnet.EndingCommand;
import org.nicocube.airain.utils.telnet.TelnetServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.db4o.Db4o;
import com.db4o.ObjectContainer;
import com.db4o.ObjectServer;
import com.db4o.messaging.MessageContext;
import com.db4o.messaging.MessageRecipient;

public class DataServer implements MessageRecipient, Server {
	private static final Logger log = LoggerFactory.getLogger(DataServer.class);
	// setting the value to true denotes that the server should be closed
	private boolean stop = false;

	/**
	 * starts a db4o server using the configuration from
	 * {@link ServerConfiguration}.
	 * @throws IOException 
	 * @throws FileNotFoundException 
	 */
	public static void main(String[] args) throws FileNotFoundException, IOException {
		Config cfg;
		if (args.length > 0) {
			cfg = ConfigProvider.inst().provide(args[0]);
		} else {
			File f = new File("dataserver.properties");
			if (f.canRead()) {
				cfg = ConfigProvider.inst().provide(f);
			} else {
				Map<String,String> m = new HashMap<String, String>();
				m.put(DataServerKeys.FILE, "default.db");
				m.put(DataServerKeys.PORT,Integer.toString(4200));
				m.put(DataServerKeys.USER, "default.user");
				m.put(DataServerKeys.PASSWORD, "default.pswd");
				m.put(TelnetServer.TelnetPortProperty, Integer.toString(4210));
				cfg = ConfigProvider.inst().provide(m);
			}
		}
		DataServer ds = new DataServer();

		TelnetServer ts = new TelnetServer(cfg.getInt(TelnetServer.TelnetPortProperty),ds);
		ts.launch();
		
		ds.runServer(cfg);
	} 
	// end main

	/**
	 * opens the ObjectServer, and waits forever until close() is called
	 * or a StopServer message is being received.
	 */
	public void runServer(Config conf){
		synchronized(this){
			log.info("entering runServer");
			ObjectServer server = Db4o.openServer(conf.get(DataServerKeys.FILE), conf.getInt(DataServerKeys.PORT));
			log.info("Db4o.openServer : OK");
			server.grantAccess(conf.get(DataServerKeys.USER), conf.get(DataServerKeys.PASSWORD));
			log.info("server.grantAccess : OK");
			// Using the messaging functionality to redirect all
			// messages to this.processMessage
			server.ext().configure().clientServer().setMessageRecipient(this);
			
			// to identify the thread in a debugger
			Thread.currentThread().setName(this.getClass().getName());
			// We only need low priority since the db4o server has
			// it's own thread.
			Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
			try {
				while(! stop){
					// wait forever for notify() from close()
					this.wait(Long.MAX_VALUE);   
				}
			} catch (Exception e) {
				log.error("Error while waiting...",e);
			}
			server.close();
		}
	}

	/**
	 * messaging callback
	 * @see com.db4o.messaging.MessageRecipient#processMessage(ObjectContainer, Object)
	 */
	@Override
	public void processMessage(MessageContext context, Object message) {
//		if(message instanceof StopServer){
//			close();
//		}
	}

	/**
	 * closes this server.
	 */
	@Command("stop")
	@EndingCommand
	@CommandDescription("Stop the server")
	public void close(){
		synchronized(this){
			stop = true;
			this.notify();
		}
	}

	@Override
	public String getName() {
		return "DataServer";
	}
}
